﻿namespace WizardEngine.Core.Specification
{
    using System;
    using System.Diagnostics.Contracts;
    using System.Collections.Generic;

    /// <summary>
    /// Specification of wizard steps connections to other ones.
    /// </summary>
    internal class StepSpecification<T> : IStepSpecification<T> where T : FlowState
    {
        public IList<TransitionSpecification<T>> transtions = new List<TransitionSpecification<T>>();

        public StepSpecification(string name, IStepOwner<T> owner)
        {
            this.Name = name;
            this.Owner = owner;
        }

        public IStepOwner<T> Owner { get; set; }
        public string Name { get; set; }
        
        /// <summary>
        /// Marks the step as initial one if possible. 
        /// </summary>
        /// <returns>The step. </returns>
        /// <exception cref="CannotCreateMoreThanOneInitialStep" />
        public IStepSpecification<T> MakeInitial()
        {
            if (this.Owner.InitialStep != null)
            {
                throw new CannotCreateMoreThanOneInitialStep();
            }

            this.Owner.InitialStep = this;
            return this;
        }

        /// <summary>
        /// Defines connection from this step to destination one by transtion with given condition. 
        /// </summary>
        /// <param name="destination">Target step. </param>
        /// <param name="condition">Transition condition. </param>
        /// <returns>Source step. </returns>
        public IStepSpecification<T> ConnectTo(IStepSpecification<T> destination, TransitionCondition<T> condition)
        {
            Contract.Assert(destination != null);
            Contract.Assert(destination as StepSpecification<T> != null);
            Contract.Assert(this.Owner.IsBindable(destination as StepSpecification<T>));
            Contract.Assert(condition != null);

            this.transtions.Add(new TransitionSpecification<T>(destination, condition));
            return this;
        }

        /// <summary>
        /// Binds the requested step to the current definition context. 
        /// </summary>
        /// <param name="stepName">Already registered step name. </param>
        /// <returns>Requested step. </returns>
        /// <exception cref="StepNotFound" />
        public IStepSpecification<T> BindStep(string stepName)
        {
            return this.Owner.BindStep(stepName);
        }

        /// <summary>
        /// Checks whether given step is possible binding result. 
        /// </summary>
        /// <param name="step">Step specification. </param>
        /// <returns>True if given step can be binded, false otherwise. </returns>
        public bool IsBindable(IStepSpecification<T> step)
        {
            Contract.Assert(step as StepSpecification<T> != null);
            return this.Owner.IsBindable(step);
        }
    }
}